package com.mc.lp.database.service.handler;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;

import com.mc.lp.database.cache.DBCacheService;
import com.mc.lp.database.entity.GradeYearMapperEntity;
import com.mc.lp.database.entity.MicroCampusEntity;
import com.mc.lp.database.service.handler.constant.FormRequestType;
import com.mc.lp.database.service.handler.constant.IFormHandler;
import com.mc.lp.database.service.handler.exception.UIExceptionList;
import com.mc.lp.service.MicroCampusEntityService;
import com.mc.lp.shared.vo.IValueObject;
import com.mc.lp.shared.vo.SearchCriteriaVO;

/**
 * Abstract class for form handling sub classes
 * @author deepak
 * @param <V>
 * @param <R>
 */
public abstract class AbstractFormHandler<V extends IValueObject, R extends IValueObject> implements IFormHandler<V, R> {
	@Resource
	public DBCacheService cacheService;
	@Resource
	MicroCampusEntityService microCampusEntityService;
	
	protected AbstractFormHandler(){
		validateFormHandler();
	}
	
	private void validateFormHandler(){
		if(CollectionUtils.isEmpty(getRequestHandlingType())){
			throw new RuntimeException("Form Handler must implement getRequestHandlingType() with correct values");
		}
	}
	
	public R handleRequest(FormRequestType requestType, V request) throws Exception {
		//remove sql injection if any
		request=removeSQLInjection(requestType, request);
		List<Exception> exceptions=validateRequest(requestType, request);
		if(CollectionUtils.isEmpty(exceptions)){
			return serveRequest(requestType, request);
		}else{
			throw new UIExceptionList(exceptions);
		}
	}
	
	protected List<Integer> getGradesByPageNumAndLimit(SearchCriteriaVO object,boolean unlimited) {
		MicroCampusEntity student = microCampusEntityService.getEntityById(object.getStudentEntityId());
		List<GradeYearMapperEntity> gradeYearMapper = new ArrayList<GradeYearMapperEntity>(student.getGradeYearMapper());
		List<Integer> gradeValueList = new ArrayList<Integer>();
		for (GradeYearMapperEntity gradeYearMap : gradeYearMapper) {
			gradeValueList.add(gradeYearMap.getSchoolGrade());
		}
		if (gradeValueList.isEmpty()) {
			return null;
		}
		return gradeValueList;
	}
	
	class GradeComparator implements Comparator<GradeYearMapperEntity> {
		@Override
		public int compare(GradeYearMapperEntity o1, GradeYearMapperEntity o2) {
			return o2.getSchoolGrade() - o1.getSchoolGrade();
		}
	}
	
	protected List<Integer> getGradesByPageNumAndLimit(SearchCriteriaVO object) {
		MicroCampusEntity student = microCampusEntityService.getEntityById(object.getStudentEntityId());

		Long limit = object.getLimit();
		Long pageNum = object.getPageNumber();

		List<GradeYearMapperEntity> gradeYearMapper = new ArrayList<GradeYearMapperEntity>(student.getGradeYearMapper());

		int collectionSize = gradeYearMapper.size();
		Collections.sort(gradeYearMapper, new GradeComparator());

		int startIndex = (int) (pageNum * limit) > collectionSize ? collectionSize : (int) (pageNum * limit);
		int endIndex = (int) ((startIndex + limit) > collectionSize ? (collectionSize) : (startIndex + limit));

		gradeYearMapper = gradeYearMapper.subList(startIndex, endIndex);

		List<Integer> gradeValueList = new ArrayList<Integer>();

		for (GradeYearMapperEntity gradeYearMap : gradeYearMapper) {
			gradeValueList.add(gradeYearMap.getSchoolGrade());
		}

		// Returning the response since there is no data for this criteria
		if (gradeValueList.isEmpty()) {
			return null;
		}
		return gradeValueList;
	}
	
	
	public abstract R serveRequest(FormRequestType requestType, V object) throws Exception;
	/**
	 * remaining implementation pending, this will remove sql injections if any 
	 */
	public V removeSQLInjection(FormRequestType requestType, V v) {
		return v;
	}
	@Override
	public List<Exception> validateRequest(FormRequestType requestType, V request) {
		return null;
	}

	public DBCacheService getCacheService() {
		return cacheService;
	}

	public void setCacheService(DBCacheService cacheService) {
		this.cacheService = cacheService;
	}
	
}
